package com.soup.lab.redis;

import com.soup.lab.tools.mtt.MultiThreadTestUtils;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RMap;
import org.redisson.api.RReadWriteLock;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;

public class RedissonUseCase {

    private static final Logger logger = LoggerFactory.getLogger(RedissonUseCase.class);

    static final RedissonClient redissonClient;
    static {
        Config config = new Config();
        config.useClusterServers()
                .addNodeAddress(
                        "redis://192.168.1.5:46379",
                        "redis://192.168.1.5:46380",
                        "redis://192.168.1.5:46381",
                        "redis://192.168.1.5:46382",
                        "redis://192.168.1.5:46383");
        redissonClient = Redisson.create(config);
    }

    @Test
    public void test() {    // 不加锁，多线程情况下结果不准确
        Counter counter = new Counter();
        MultiThreadTestUtils.run("counter", 10, () -> {
            counter.incr();
        });
        System.out.println(counter.count);  // 可能不为10
    }

    @Test
    public void testLock() {    // 加锁后结果准确
        RLock lock = redissonClient.getLock("redisson:test:lock");
        Counter counter = new Counter();
        MultiThreadTestUtils.run("counter", 10, () -> {
            lock.lock();
            try {
                logger.info("加锁成功");
                counter.incr();
                TimeUnit.SECONDS.sleep(4);
            } catch (InterruptedException ignore) {
            } finally {
                lock.unlock();
                logger.info("解锁成功");
            }
        });
        Assertions.assertEquals(10, counter.count);
    }

    @Test
    public void testLock2() {    // 和testLock同时启动，模拟多进程
        testLock();
    }


    static class Counter {
        int count;

        public void incr() {
            count++;
        }
    }

    @Test
    public void testReadLock() {
        RReadWriteLock readWriteLock = redissonClient.getReadWriteLock("redisson:test:read-write-lock");
        MultiThreadTestUtils.run(3, () -> {
            while(true) {
                readWriteLock.readLock().lock();
                try {
                    logger.info("获取到了读锁");
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    readWriteLock.readLock().unlock();
                }
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
    }

    @Test
    public void testWriteLock() {
        RReadWriteLock readWriteLock = redissonClient.getReadWriteLock("redisson:test:read-write-lock");
        MultiThreadTestUtils.run(3, () -> {
            while(true) {
                readWriteLock.writeLock().lock();
                try {
                    logger.info("获取到了写锁");
                    TimeUnit.SECONDS.sleep(2);
                } catch (InterruptedException e) {

                } finally {
                    readWriteLock.writeLock().unlock();
                }
                try {
                    TimeUnit.SECONDS.sleep(3);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
    }

    @Test
    public void testMultiLock() throws InterruptedException {
        RLock lock1 = redissonClient.getLock("redisson:test:1");
        RLock lock2 = redissonClient.getLock("redisson:test:2");
        RLock multiLock = redissonClient.getMultiLock(lock1, lock2);
        MultiThreadTestUtils.runAsync("lock1-thread", 1, () -> {
            while(true) {
                lock1.lock();
                try {
                    logger.info("获取到了lock1");
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {

                } finally {
                    lock1.unlock();
                }
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        MultiThreadTestUtils.runAsync("lock2-thread", 1, () -> {
            while(true) {
                lock2.lock();
                try {
                    logger.info("获取到了lock2");
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {

                } finally {
                    lock2.unlock();
                }
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        MultiThreadTestUtils.runAsync("multiLock-thread", 1, () -> {
            while(true) {
                multiLock.lock();
                try {
                    logger.info("获取到了multiLock");
                    TimeUnit.SECONDS.sleep(2);
                } catch (InterruptedException e) {
                } finally {
                    multiLock.unlock();
                }
                try {
                    TimeUnit.SECONDS.sleep(2);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        TimeUnit.MINUTES.sleep(3);
    }

    @Test
    public void testMultiLock2() throws InterruptedException {
        testMultiLock();
    }

    public <V> V atomic(String key, Callable<V> callable) throws Exception {
        RLock lock = redissonClient.getLock(key);
        lock.lock();
        try {
            return callable.call();
        } catch (Exception e) {
            throw e;
        } finally {
            lock.unlock();
        }
    }

    /**
     * 测试 watchDog 在默认配置下
     *  未指定leaseTime的lock，锁的有效期是30s，watchDog每10秒将过期时间延长至30秒
     *  指定leaseTime的lock，锁的有效期就是leaseTime，watchDog不生效，即不会延长有效期
     */
    @Test
    public void testWatchDog() throws InterruptedException {
        RLock lock1 = redissonClient.getLock("redisson:test:1");
        lock1.lock(4000, TimeUnit.MILLISECONDS);
        RLock lock2 = redissonClient.getLock("redisson:test:2");
        lock2.lock();
        TimeUnit.SECONDS.sleep(40);
    }

    /*
     * 测试强制解锁，先启动 #testForceUnlock1，redis中查看key的状态，再启动 testForceUnlock2
     * 强制解锁成功
     */

    @Test
    public void testForceUnlock1() throws InterruptedException {
        RLock lock1 = redissonClient.getLock("redisson:test:1");
        lock1.lock(5, TimeUnit.MINUTES);
        TimeUnit.MINUTES.sleep(5);
    }
    @Test
    public void testForceUnlock2() {
        RLock lock1 = redissonClient.getLock("redisson:test:1");
        System.out.println("isLocked: "+ lock1.isLocked());                             // true
        System.out.println("isHeldByCurrentThread:"+ lock1.isHeldByCurrentThread());    // false
        System.out.println("forceUnlock:"+ lock1.forceUnlock());                        // true
    }

    @Test
    public void testMap() {
        RMap<String, String> map = redissonClient.getMap("redisson:test:map");


    }



}
